package com.kerbores.mongo.dao;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

import org.nutz.lang.Lang;

import com.kerbores.mongo.EntityMapping;
import com.kerbores.mongo.utils.Mongos;
import com.kerbores.utils.web.pager.Pager;
import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;

public abstract class MongoDao<T> implements IMongoDao<T> {

	public MongoDao() {
	}

	@Override
	public boolean save(T t) {
		return getCollection().save(getMapping().toDBObject(t)).getN() == 1;
	}

	@Override
	public int remove(T t) {
		return getCollection().remove(getMapping().toDBObject(t)).getN();
	}

	@Override
	public int removeById(long id) {
		return getCollection().remove(new BasicDBObject().append("_id", id)).getN();
	}

	@Override
	public int removeByCnd(Object obj) {
		if (!(obj instanceof DBObject)) {
			obj = Mongos.obj2dbo(obj);
		}
		return getCollection().remove((DBObject) obj).getN();
	}

	@Override
	public boolean update(T t) {
		long id = 0;
		try {
			Field field = t.getClass().getDeclaredField("id");
			if (!field.isAccessible()) {
				field.setAccessible(true);
			}
			id = Long.parseLong(field.get(t).toString());
		} catch (Exception e) {
			e.printStackTrace();
			throw Lang.wrapThrow(e);
		}
		return updateById(id, t);
	}

	@Override
	public boolean updateById(long id, T t) {
		DBObject q = new BasicDBObject("_id", id);
		DBObject o = getMapping().toDBObject(t);
		return getCollection().update(q, o).getN() == 1;
	}

	@Override
	public int updateByCnd(Object obj, T t) {
		if (!(obj instanceof DBObject)) {
			obj = Mongos.obj2dbo(obj);
		}
		return getCollection().update((DBObject) obj, getMapping().toDBObject(t)).getN();
	}

	@Override
	public int updateFieldByCnd(Object obj, Object fieldDes) {
		DBObject q = null;
		if (obj instanceof DBObject) {
			q = (DBObject) obj;
		} else {
			q = Mongos.obj2dbo(obj);
		}
		DBObject o = null;
		if (fieldDes instanceof DBObject) {
			o = (DBObject) fieldDes;
		} else {
			o = Mongos.obj2dbo(fieldDes);
		}
		return getCollection().updateMulti(q, o).getN();
	}

	@Override
	public int updateFieldById(long id, Object fieldDes) {
		return updateFieldByCnd(new BasicDBObject("_id", id), fieldDes);
	}

	@Override
	public long count() {
		return getCollection().count();
	}

	@Override
	public long countByCnd(Object obj) {
		DBObject query = null;
		if (obj instanceof DBObject) {
			query = (DBObject) obj;
		} else {
			query = Mongos.obj2dbo(obj);
		}
		return getCollection().count(query);
	}

	@Override
	public List<T> listAll() {
		List<T> ts = new ArrayList<T>();
		DBCursor cursor = getCollection().find();
		while (cursor.hasNext()) {
			ts.add(getMapping().get(cursor.next()));
		}
		return ts;
	}

	@Override
	public List<T> findByCnd(Object obj) {
		DBObject query = null;
		if (obj instanceof DBObject) {
			query = (DBObject) obj;
		} else {
			query = Mongos.obj2dbo(obj);
		}
		List<T> ts = new ArrayList<T>();
		DBCursor cursor = getCollection().find(query);
		while (cursor.hasNext()) {
			ts.add(getMapping().get(cursor.next()));
		}
		return ts;
	}

	@Override
	public T findOneByCnd(Object obj) {
		DBObject query = null;
		if (obj instanceof DBObject) {
			query = (DBObject) obj;
		} else {
			query = Mongos.obj2dbo(obj);
		}
		DBObject object = getCollection().findOne(query);
		if (object == null) {
			return null;
		}
		return getMapping().get(object);
	}

	@Override
	public T findById(long id) {
		DBObject q = new BasicDBObject("_id", id);
		DBObject object = getCollection().findOne(q);
		if (object == null) {
			return null;
		}
		return getMapping().get(object);
	}

	@Override
	public Pager listAllByPage(Pager pager) {
		List<T> ts = new ArrayList<T>();
		DBCursor cursor = getCollection().find().skip(pager.start()).limit(pager.getPageSize());
		while (cursor.hasNext()) {
			ts.add(getMapping().get(cursor.next()));
		}
		pager.setEntities(ts);
		pager.setCount(count());
		return pager;
	}

	@Override
	public Pager findByPage(Object obj, Pager pager) {
		DBObject query = null;
		if (obj instanceof DBObject) {
			query = (DBObject) obj;
		} else {
			query = Mongos.obj2dbo(obj);
		}
		List<T> ts = new ArrayList<T>();
		DBCursor cursor = getCollection().find(query).skip(pager.start()).limit(pager.getPageSize());
		while (cursor.hasNext()) {
			ts.add(getMapping().get(cursor.next()));
		}
		pager.setEntities(ts);
		pager.setCount(countByCnd(query));
		return pager;
	}

	/**
	 * 获取mapping接口实例
	 * 
	 * @return
	 */
	protected abstract EntityMapping<T> getMapping();

	/**
	 * 获取集合实例
	 * 
	 * @return
	 */
	protected abstract DBCollection getCollection();

}
